use std::ops::Deref;
use std::sync::Arc;

use super::container::item::ContainerReadItem;
use super::chunk_meta::ChunkLayout;

use crate::chunk_meta::ChunkKind;
use crate::constants::DefaultFingerprintType;
use crate::recipe::FpPointer;
use util::{Cp,UnsafeBuf,MakeCp};




pub enum ReChunk {
	Unique(UniqueReChunk),
	Delta(DeltaReChunk),
}

impl ReChunk {
	pub fn unique(layout:Cp<ChunkLayout>,cp:Cp<[u8]>) -> Self {
		ReChunk::Unique(UniqueReChunk::new(layout,cp))
	}

	pub fn delta(meta:Cp<ChunkLayout>,base_pointer:Cp<FpPointer>,cp:Cp<[u8]>) -> Self {
		ReChunk::Delta(DeltaReChunk::new(meta,base_pointer,cp))
	}

	pub fn is_unique(&self) -> bool {
		if let Self::Unique(_) = self {
			true
		} else {
			false
		}
	}

	pub fn unique_unwrap(&self) -> Option<&UniqueReChunk> {
		if let Self::Unique(rc) = self {
			Some(rc)
		} else {
			None
		}
	}
	
	pub fn fp(&self) -> &DefaultFingerprintType {
		match self {
			ReChunk::Unique(rc) => rc.fp(),
			ReChunk::Delta(rc) => rc.fp(),
		}
	}
}

impl Deref for ReChunk {
	type Target = [u8];
	fn deref(&self) -> &Self::Target {
		match self {
			ReChunk::Unique(rc) => &rc,
			ReChunk::Delta(rc) => &rc,
		}
	}
}

pub struct UniqueReChunk {
	data:Cp<[u8]>,
	layout:Cp<ChunkLayout>,
}

impl UniqueReChunk {
	pub fn new(meta:Cp<ChunkLayout>,cp:Cp<[u8]>) -> Self {
		Self {
			data:cp,
			layout:meta,
		}
	}
	
	pub fn fp(&self) -> &DefaultFingerprintType {
		self.layout.fp()
	}
}

impl Deref for UniqueReChunk {
	type Target = [u8];
	fn deref(&self) -> &Self::Target {
		&self.data
	}
}

pub struct DeltaReChunk {
	delta:Cp<[u8]>,
	base_pointer:Cp<FpPointer>,
	layout:Cp<ChunkLayout>,
}

impl DeltaReChunk {
	pub fn new(meta:Cp<ChunkLayout>,base_pointer:Cp<FpPointer>,cp:Cp<[u8]>) -> Self {
		Self {
			delta:cp,
			base_pointer:base_pointer,
			layout:meta,
		}
	}

	pub fn base_chunk_pointer(&self) -> &FpPointer {
		&self.base_pointer
	}
	
	pub fn fp(&self) -> &DefaultFingerprintType {
		self.layout.fp()
	}

	pub fn origin_max_len(&self) -> usize {
		0xffffffff
	}

	pub fn restore(&self,base:&UniqueReChunk) -> Option<Vec<u8>> {
		//println!("{:?} + {:?}",self as &[u8],base as &[u8]);
		xdelta3::decode(&self, &base, self.origin_max_len())
	}
}

impl Deref for DeltaReChunk {
	type Target = [u8];
	fn deref(&self) -> &Self::Target {
		&self.delta
	}
}

impl ContainerReadItem for ReChunk {
	fn read_slice_from(buf:&Arc<UnsafeBuf>) -> Option<Arc<Self>> {
		let layout:Cp<ChunkLayout> = buf.make_const_pointer().unwrap();
		let data_len = layout.data_len();
		match layout.kind() {
			ChunkKind::Unique => {
				Some(Arc::new(ReChunk::unique(
					layout,
					buf.make_const_slice(data_len).unwrap(),
				)))
			}
			ChunkKind::Delta => {
				Some(Arc::new(ReChunk::delta(
					layout,
					buf.make_const_pointer().unwrap(),
					buf.make_const_slice(data_len).unwrap(),
				)))
			}
		}
	}
	
	fn len(&self) -> usize {
		(self as &[u8]).len()
	}
}
